package wiki.xsx.core.handler;

import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import wiki.xsx.core.util.ApplicationContextUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentMap;

/**
 * 通用助手
 * @author xsx
 * @date 2019/4/25
 * @since 1.8
 */
public class CommonHandler {
    /**
     * 助手管理实例
     */
    private static final HandlerManager MANAGER = HandlerManager.getManager();
    /**
     * 通用助手实例
     */
    private static final CommonHandler HANDLER = new CommonHandler();

    /**
     * 通用助手构造
     */
    private CommonHandler() {
        int dbIndex = getInitDBIndex();
        initDBHandler(dbIndex);
        initKeyHandler(dbIndex);
        initNumberHandler(dbIndex);
        initStringHandler(dbIndex);
        initListHandler(dbIndex);
        initHashHandler(dbIndex);
        initSetHandler(dbIndex);
        initZsetHandler(dbIndex);
        initBitmapHandler(dbIndex);
        initGeoHandler(dbIndex);
        initHyperLogLogHandler(dbIndex);
        initScriptHandler(dbIndex);
        initPubSubHandler(dbIndex);
        initSentinelHandler(dbIndex);
        initCustomCommandHandler(dbIndex);
    }

    /**
     * 获取助手实例
     * @return 返回助手实例
     */
    public static CommonHandler getInstance() {
        return HANDLER;
    }

    /**
     * 获取默认KEY
     * @return 返回默认KEY
     */
    public String getDefaultKey() {
        return MANAGER.getDefaultKey();
    }

    /**
     * 获取助手
     * @param key KEY
     * @param type 助手类型
     * @return 返回助手
     */
    public RedisHandler getHandler(String key, HandlerType type) {
        // 若是集群助手类型，则直接返回
        if (type==HandlerType.CLUSTER) {
            return MANAGER.getClusterHandler();
        }
        ConcurrentMap<String, RedisHandler> map = MANAGER.getHandlerMap(type);
        RedisHandler handler = map.get(key);
        if (handler!=null) {
            return handler;
        }
        synchronized (MANAGER) {
            handler = map.get(key);
            if (handler==null) {
                RedisHandler instance = this.getHandlerInstance(key, type);
                handler = map.putIfAbsent(key, instance);
                if (handler==null) {
                    handler = instance;
                }
            }
        }
        return handler;
    }

    /**
     * 获取默认对象模板
     * @return 返回对象模板
     */
    public RedisTemplate<String, Object> getDefaultRedisTemplate() {
        return HandlerManager.getDefaultRedisTemplate();
    }

    /**
     * 获取默认字符串模板
     * @return 返回字符串模板
     */
    public StringRedisTemplate getDefaultStringRedisTemplate() {
        return HandlerManager.getDefaultStringRedisTemplate();
    }

    /**
     * 获取初始化数据库索引
     * @return 返回数据库索引
     */
    private int getInitDBIndex() {
        return ApplicationContextUtil.getContext().getBean(RedisProperties.class).getDatabase();
    }

    /**
     * 初始化数据库助手
     * @param dbIndex 数据库索引
     */
    private void initDBHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> dbMap = MANAGER.getHandlerMap(HandlerType.DB);
        DBHandler handler = new DBHandler(dbIndex);
        dbMap.put(getDefaultKey(), handler);
        dbMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化键助手
     * @param dbIndex 数据库索引
     */
    private void initKeyHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> keyMap = MANAGER.getHandlerMap(HandlerType.KEY);
        KeyHandler handler = new KeyHandler(dbIndex);
        keyMap.put(getDefaultKey(), handler);
        keyMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化数字助手
     * @param dbIndex 数据库索引
     */
    private void initNumberHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> numberMap = MANAGER.getHandlerMap(HandlerType.NUMBER);
        NumberHandler handler = new NumberHandler(dbIndex);
        numberMap.put(getDefaultKey(), handler);
        numberMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化字符串助手
     * @param dbIndex 数据库索引
     */
    private void initStringHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> stringMap = MANAGER.getHandlerMap(HandlerType.STRING);
        StringHandler handler = new StringHandler(dbIndex);
        stringMap.put(getDefaultKey(), handler);
        stringMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化列表助手
     * @param dbIndex 数据库索引
     */
    private void initListHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> listMap = MANAGER.getHandlerMap(HandlerType.LIST);
        ListHandler handler = new ListHandler(dbIndex);
        listMap.put(getDefaultKey(), handler);
        listMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化哈希助手
     * @param dbIndex 数据库索引
     */
    private void initHashHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> hashMap = MANAGER.getHandlerMap(HandlerType.HASH);
        HashHandler handler = new HashHandler(dbIndex);
        hashMap.put(getDefaultKey(), handler);
        hashMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化无序集合助手
     * @param dbIndex 数据库索引
     */
    private void initSetHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> setMap = MANAGER.getHandlerMap(HandlerType.SET);
        SetHandler handler = new SetHandler(dbIndex);
        setMap.put(getDefaultKey(), handler);
        setMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化有序集合助手
     * @param dbIndex 数据库索引
     */
    private void initZsetHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> zsetMap = MANAGER.getHandlerMap(HandlerType.ZSET);
        ZsetHandler handler = new ZsetHandler(dbIndex);
        zsetMap.put(getDefaultKey(), handler);
        zsetMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化位图助手
     * @param dbIndex 数据库索引
     */
    private void initBitmapHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> bitmapMap = MANAGER.getHandlerMap(HandlerType.BITMAP);
        BitmapHandler handler = new BitmapHandler(dbIndex);
        bitmapMap.put(getDefaultKey(), handler);
        bitmapMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化地理位置助手
     * @param dbIndex 数据库索引
     */
    private void initGeoHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> geoMap = MANAGER.getHandlerMap(HandlerType.GEO);
        GeoHandler handler = new GeoHandler(dbIndex);
        geoMap.put(getDefaultKey(), handler);
        geoMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化基数助手
     * @param dbIndex 数据库索引
     */
    private void initHyperLogLogHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> hyperLogLogMap = MANAGER.getHandlerMap(HandlerType.HYPERLOGLOG);
        HyperLogLogHandler handler = new HyperLogLogHandler(dbIndex);
        hyperLogLogMap.put(getDefaultKey(), handler);
        hyperLogLogMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化lua脚本助手
     * @param dbIndex 数据库索引
     */
    private void initScriptHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> scriptMap = MANAGER.getHandlerMap(HandlerType.SCRIPT);
        ScriptHandler handler = new ScriptHandler(dbIndex);
        scriptMap.put(getDefaultKey(), handler);
        scriptMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化发布订阅助手
     * @param dbIndex 数据库索引
     */
    private void initPubSubHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> pubSubMap = MANAGER.getHandlerMap(HandlerType.PUBSUB);
        PubSubHandler handler = new PubSubHandler(dbIndex);
        pubSubMap.put(getDefaultKey(), handler);
        pubSubMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化分布式锁助手
     * @param dbIndex 数据库索引
     */
    private void initRedisLockHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> lockMap = MANAGER.getHandlerMap(HandlerType.REDISLOCK);
        RedisLockHandler handler = new RedisLockHandler(dbIndex);
        lockMap.put(getDefaultKey(), handler);
        lockMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化哨兵助手
     * @param dbIndex 数据库索引
     */
    private void initSentinelHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> sentinelMap = MANAGER.getHandlerMap(HandlerType.SENTINEL);
        SentinelHandler handler = new SentinelHandler(dbIndex);
        sentinelMap.put(getDefaultKey(), handler);
        sentinelMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 初始化自定义命令助手
     * @param dbIndex 数据库索引
     */
    private void initCustomCommandHandler(int dbIndex) {
        ConcurrentMap<String, RedisHandler> customCommandMap = MANAGER.getHandlerMap(HandlerType.CUSTOMCOMMAND);
        CustomCommandHandler handler = new CustomCommandHandler(dbIndex);
        customCommandMap.put(getDefaultKey(), handler);
        customCommandMap.put(String.valueOf(dbIndex), handler);
    }

    /**
     * 获取助手实例
     * @param key KEY
     * @param type 助手类型
     * @return 返回实例
     */
    @SuppressWarnings("unchecked")
    private RedisHandler getHandlerInstance(String key, HandlerType type) {
        Class clz = type.getTypeClass();
        try {
            Constructor constructor = clz.getDeclaredConstructor(Integer.class);
            constructor.setAccessible(true);
            return (RedisHandler) constructor.newInstance(Integer.valueOf(key));
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

}
